package cn.thoughtworks.school.programCenter.services;

import cn.thoughtworks.school.programCenter.entities.Tag;
import cn.thoughtworks.school.programCenter.entities.User;
import cn.thoughtworks.school.programCenter.entities.UserTag;
import cn.thoughtworks.school.programCenter.entities.vo.StudentTag;
import cn.thoughtworks.school.programCenter.exceptions.BusinessException;
import cn.thoughtworks.school.programCenter.repositories.TagRepository;
import cn.thoughtworks.school.programCenter.repositories.UserTagRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserTagService {
    @Autowired
    private TagRepository tagRepository;
    @Autowired
    private UserTagRepository userTagRepository;
    @Autowired
    private UserCenterService userCenterService;

    @Transactional
    public void editTags(Long programId, Long studentId, Long operatorId, List<String> tags) {
        List<UserTag> userTags = userTagRepository.findByProgramIdAndStudentId(programId, studentId);
        List<String> tagsName = userTags.stream().map(item -> item.getTag().getName()).collect(Collectors.toList());

        List<String> shouldAdd = tags.stream().filter(item -> !tagsName.contains(item)).collect(Collectors.toList());
        List<UserTag> shouldDelete = userTags.stream().filter(item -> !tags.contains(item.getTag().getName())).collect(Collectors.toList());

        userTagRepository.deleteAll(shouldDelete);
        userTagRepository.saveAll(
                shouldAdd.stream().map(item -> {
                    Tag tag = tagRepository.findByName(item).findFirst().orElse(new Tag(item, new Date(), operatorId));
                    return new UserTag(studentId, new Date(), operatorId, tag, programId);
                }).collect(Collectors.toList())
        );
    }

    public List<StudentTag> findTagsByProgramId(Long programId) {
        ObjectMapper oMapper = new ObjectMapper();
        List<Tag> tags = tagRepository.findByProgramId(programId);
        List<String> ids = tags.stream().map(item -> item.getCreatorId().toString()).distinct().collect(Collectors.toList());
        List<Map> users = userCenterService.getUsersByIds(String.join(",", ids));

        return tags.stream().map(item -> {
            Map userInfo = users.stream()
                    .filter(user -> Long.valueOf(user.get("id").toString()).equals(item.getCreatorId()))
                    .findFirst().get();
            StudentTag studentTag = oMapper.convertValue(item, StudentTag.class);
            studentTag.updateCreator(userInfo.get("username").toString());
            return studentTag;
        }).collect(Collectors.toList());
    }

    public Tag updateTag(Long tagId, Tag tag) throws BusinessException {
        Tag currentTag = tagRepository.findById(tagId).orElseThrow(() -> new BusinessException("current tag is not exist."));
        currentTag.update(tag);
        return tagRepository.save(currentTag);
    }

    public Tag addTag(Tag tag, User current) {
        tag.updateCreator(current);
        return tagRepository.save(tag);
    }

    @Transactional
    public void deleteTag(Long tagId) throws BusinessException {
        Tag tag = tagRepository.findById(tagId).orElseThrow(() -> new BusinessException("current tag is not exist"));

        deleteReferenceTags(tagId);
        tagRepository.delete(tag);
    }

    private void deleteReferenceTags(Long tagId) {
        userTagRepository.deleteByTagId(tagId);
    }

    public List<UserTag> findTagsByProgramIdAndUserId(Long programId, Long id) {
        return userTagRepository.findByProgramIdAndStudentId(programId, id);
    }

    @Transactional
    public void editStudentTags(Long programId, Long studentId, String tagIdsStr, User current) {
        List<Long> tagIds = convertTagIds(tagIdsStr);
        userTagRepository.deleteByProgramIdAndStudentId(programId, studentId);

        List<UserTag> userTags = wrapUserTags(programId, studentId, current.getId(), tagIds);
        userTagRepository.saveAll(userTags);
    }

    private List<UserTag> wrapUserTags(Long programId, Long studentId, Long creatorId, List<Long> tagIds) {
        return tagIds.stream().map((tagId -> UserTag.wrap(programId, studentId, creatorId, tagId)))
                .collect(Collectors.toList());
    }

    private List<Long> convertTagIds(String tagIdsStr) {
        return Arrays.stream(tagIdsStr.split(",")).map(Long::parseLong).collect(Collectors.toList());
    }
}
